package com.qqtech.pay.wx;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.qqtech.core.common.constant.CoreConst;
import com.qqtech.core.common.exception.PayException;
import com.qqtech.core.common.util.JsonUtil;
import com.qqtech.core.common.util.StringUtil;

/**
 * 微信处理类
 * 
 * @author andy.wangzhh
 *
 */
public class WxpayCore {
	public final static Logger logger = LoggerFactory.getLogger(WxpayCore.class);

	private static final CloseableHttpClient httpclient = HttpClients.createDefault();
	/**
	 * 配置工具
	 */
	private static final WxpayCfg config = WxpayCfg.getInstance();

	private static WxpayCore instance = new WxpayCore();

	private WxpayCore() {
	}

	public static WxpayCore getInstance() {
		return instance;
	}

	/**
	 * 请求wx接口获取预支付id
	 * 
	 * @param url
	 * @param xmlParam
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public String getPrepayData(String url, String xmlParam) {
		HttpPost httpost = new HttpPost(url);
		String prepayId = "";
		String jsonStr = "";
		try {
			httpost.setEntity(new StringEntity(xmlParam, CoreConst.DEFAULT_CODETYPE));
			HttpResponse response = httpclient.execute(httpost);
			jsonStr = EntityUtils.toString(response.getEntity(), CoreConst.DEFAULT_CODETYPE);
			if (jsonStr.indexOf("FAIL") != -1) {
				throw new PayException(jsonStr);
			}
			Map map = doXMLParse(jsonStr);
			prepayId = (String) map.get("prepay_id");
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("请求微信接口获取预支付ID出错,微信返回信息：" + jsonStr);
			return null;
		}
		return prepayId;
	}

	/**
	 * 解析xml,返回第一级元素键值对。如果第一级元素有子节点，则此节点的值是子节点的xml数据。
	 * 
	 * @param strxml
	 * @return
	 * @throws IOException
	 */
	@SuppressWarnings("rawtypes")
	private Map doXMLParse(String strxml) throws IOException {
		if (null == strxml || "".equals(strxml)) {
			return null;
		}
		Map<String, String> m = new HashMap<String, String>();
		InputStream in = String2Inputstream(strxml);
		SAXBuilder builder = new SAXBuilder();
		Document doc;
		try {
			doc = builder.build(in);
			Element root = doc.getRootElement();
			List list = root.getChildren();
			Iterator it = list.iterator();
			while (it.hasNext()) {
				Element e = (Element) it.next();
				String k = e.getName();
				String v = "";
				List children = e.getChildren();
				if (children.isEmpty()) {
					v = e.getTextNormalize();
				} else {
					v = getChildrenText(children);
				}
				m.put(k, v);
			}
		} catch (JDOMException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} finally {
			if (null != in) {
				in.close();
			}
		}
		return m;
	}

	/**
	 * 获取子结点的xml
	 * 
	 * @param children
	 * @return String
	 */
	@SuppressWarnings("rawtypes")
	private String getChildrenText(List children) {
		StringBuffer sb = new StringBuffer();
		if (!children.isEmpty()) {
			Iterator it = children.iterator();
			while (it.hasNext()) {
				Element e = (Element) it.next();
				String name = e.getName();
				String value = e.getTextNormalize();
				List list = e.getChildren();
				sb.append("<" + name + ">");
				if (!list.isEmpty()) {
					sb.append(getChildrenText(list));
				}
				sb.append(value);
				sb.append("</" + name + ">");
			}
		}
		return sb.toString();
	}

	/**
	 * 转换流
	 * 
	 * @param str
	 * @return
	 */
	private InputStream String2Inputstream(String str) {
		return new ByteArrayInputStream(str.getBytes());
	}

	/**
	 * 生成随机字符串
	 * 
	 * @return
	 */
	public String getNonceStr() {
		Date now = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSSS");
		String s = sdf.format(now);
		return StringUtil.md5Sign(s, config.getValue("inputCharset", CoreConst.DEFAULT_CODETYPE));
	}

	/**
	 * 签名
	 * 
	 * @param paramMap
	 * @return
	 */
	public String createSign(Map<String, Object> params) {
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			Object value = params.get(key);
			if ("sign".equals(key) || value == null || StringUtil.isBlank(value.toString())) {
				continue;
			}
			sb.append(key).append("=").append(value).append("&");
		}
		sb.append("key=").append(config.getValue("apiKey"));
		String sign = StringUtil.md5Sign(sb.toString(), config.getValue("inputCharset", CoreConst.DEFAULT_CODETYPE));
		return sign.toUpperCase();
	}

	/**
	 * 生成请求数据
	 * 
	 * @param paramMap
	 * @return
	 */
	public String createXml(Map<String, Object> paramMap) {
		StringBuilder sb = new StringBuilder(512);
		Entry<String, Object> entry = null;
		sb.append("<xml>");
		for (Iterator<Entry<String, Object>> it = paramMap.entrySet().iterator(); it.hasNext();) {
			entry = it.next();
			sb.append("<").append(entry.getKey()).append("><![CDATA[");
			sb.append(entry.getValue());
			sb.append("]]></").append(entry.getKey()).append(">");
		}
		sb.append("</xml>");
		return sb.toString();
	}

	/**
	 * 解析参数
	 * 
	 * @param paramMap
	 * @return
	 * @throws IOException
	 */
	public Map<String, Object> parseParam(HttpServletRequest request) throws IOException {
		// 解析结果存储在HashMap
		InputStream inputStream = null;
		Map<String, Object> map = null;
		try {
			map = new TreeMap<String, Object>();
			inputStream = request.getInputStream();
			// 读取输入流
			SAXBuilder builder = new SAXBuilder();
			Document document = builder.build(inputStream);
			// 得到xml根元素
			Element root = document.getRootElement();
			// 得到根元素的所有子节点
			List<Element> elementList = root.getChildren();
			// 遍历所有子节点
			for (Element e : elementList) {
				map.put(e.getName(), e.getText());
			}
		} catch (Exception ex) {
			throw new IOException("解析请求参数时出错", ex);
		} finally {
			if (null != inputStream) {
				inputStream.close();
			}
		}
		return map;
	}

	/**
	 * 验证
	 * 
	 * @param paramMap
	 * @return
	 */
	public boolean verify(Map<String, Object> paramMap) {
		if (!"SUCCESS".equals(paramMap.get("result_code"))) {// 失败
			logger.error("微信支付异步通知：状态不成功,验证失败。{}", JsonUtil.toJson(paramMap));
			return false;
		}
		if (!paramMap.containsKey("sign")) {// 失败
			logger.error("微信支付异步通知：为包含sign信息,验证失败。{}", JsonUtil.toJson(paramMap));
			return false;
		}
		String strSign = (String) paramMap.get("sign");
		String strGenSign = createSign(paramMap);
		if (!strSign.equals(strGenSign)) {
			logger.error("微信支付异步通知：签名错误,验证失败。{}", JsonUtil.toJson(paramMap));
			return false;
		}
		return true;
	}

	/**
	 * 微信异步通知返回
	 * 
	 * @param isSuccess
	 * @return
	 */
	public String notifyBack(boolean isSuccess) {
		String result = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
		if (!isSuccess) {
			result = "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";
		}
		return result;
	}

	public static void main(String[] s) {
		String t = "D5:BF:11:CD:31:D4:F3:F1:E7:2E:AA:04:D5:A5:9B:E4";
		System.out.println(t.replaceAll(":", "").toLowerCase());
	}
}